package com.yao.mall.service.impl;


import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yao.mall.common.EmailUtils;
import com.yao.mall.common.ErrorCode;
import com.yao.mall.common.PageRequest;
import com.yao.mall.config.AliyunOSSConfig;
import com.yao.mall.config.BusinessException;
import com.yao.mall.config.SendSmsConfig;
import com.yao.mall.mapper.UserMapper;
import com.yao.mall.common.UserHolder;
import com.yao.mall.model.dto.UserRequest;
import com.yao.mall.model.dto.UserSearchRequest;
import com.yao.mall.model.entity.ShoppingOrder;
import com.yao.mall.model.entity.User;
import com.yao.mall.model.vo.ShoppingOrderVO;
import com.yao.mall.model.vo.UserVO;
import com.yao.mall.service.UserService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static com.yao.mall.constant.UserConstant.*;

/**
 * @author jiahua.yao
 * @description 针对表【user】的数据库操作Service实现
 * @date 2024-02-29 13:06:36
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService {
    @Resource
    private UserMapper userMapper;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private EmailUtils emailUtils;

    @Override
    public UserVO userLogin(UserRequest userRequest) {
        String loginAdapter = getAdapter(userRequest);
        // 1.1 选择登录方式
        User user = switch (loginAdapter) {
            case BY_PASSWORD -> this.loginByPassword(userRequest);
            case BY_EMAIL -> this.loginByEmail(userRequest);
            case BY_PHONE -> this.loginByPhone(userRequest);
            default -> throw new BusinessException(ErrorCode.PARAMS_ERROR, "登录方式有误！");
        };
        if(user.getIsBan() == 1){
            throw new BusinessException(ErrorCode.OPERATION_ERROR,"改账户已被禁用，请联系管理员处理！");
        }
        //1.2 随机token作为登录令牌
        String token = UUID.randomUUID().toString(true);
        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(user, userVO);
        userVO.setToken(token);
        //1.3 存储并设置token有效期
        String tokenKey = LOGIN_USER_KEY + token;
        String jsonStr = JSONUtil.toJsonStr(userVO);
        stringRedisTemplate.opsForValue().set(tokenKey, jsonStr, 30, TimeUnit.MINUTES);
        return userVO;
    }


    @Override
    public User loginByPassword(UserRequest userRequest) {
        //账号可以是account、phone、mail,但要注意，前端这三者都是以账号来传输数据的
        String account = userRequest.getAccount();
        String password = userRequest.getPassword();
        // 1.判断
        if (StringUtils.isAnyBlank(account,password)) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "账号密码不能为空");
        }
        // 2. 加密
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + password).getBytes());
        // 3. 账号类型
        // 查询用户是否存在
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("account", account)
                .or().eq("mail",account)
                .or().eq("phone",account);
        User user = userMapper.selectOne(queryWrapper);
        if (user == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "用户不存在请注册！");
        }
        // 查询用户是否存在
        if (!encryptPassword.equals(user.getPassword())) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "密码错误");
        }
        return user;
    }

    @Override
    public User loginByEmail(UserRequest userRequest) {
        String mail = userRequest.getMail();
        String verCode = userRequest.getVerCode();
        if (StringUtils.isAnyBlank(mail, verCode)) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "邮箱和验证码不能为空！");
        }
        User user = getSelectUser(BY_EMAIL, mail);
        String verCodeBefore = stringRedisTemplate.opsForValue().get(VER_CODE + mail);
        if (!verCode.equals(verCodeBefore)) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "验证码有误或失效！");
        }
        return user;
    }

    @Override
    public User loginByPhone(UserRequest userRequest) {
        String phone = userRequest.getPhone();
        String verCode = userRequest.getVerCode();
        if (StringUtils.isAnyBlank(phone, verCode)) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "号码和验证码不能为空！");
        }
        User user = getSelectUser(BY_PHONE, phone);
        String verCodeBefore = stringRedisTemplate.opsForValue().get(VER_CODE + phone);
        if (!verCode.equals(verCodeBefore)) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "验证码有误或失效！");
        }
        return user;
    }


    /**
     * 判断三种登录情况下用户是否存在
     */
    private User getSelectUser(String condition, String value) {
        // 查询用户是否存在
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(condition, value);
        User user = userMapper.selectOne(queryWrapper);
        if (user == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "用户不存在请注册！");
        }
        return user;
    }

    @Override
    public boolean sendLoginCode(UserRequest userRequest) {
        //1.判断是手机号还是邮箱号
        String loginAdapter = getAdapter(userRequest);
        //2.1 验证码
        if (BY_EMAIL.equals(loginAdapter)) {
            return sendRegEmail(userRequest);
        }
        if(BY_PHONE.equals(loginAdapter)){
            return sendPhoneCode(userRequest);
        }
        throw new BusinessException(ErrorCode.OPERATION_ERROR,"未选择验证码类型");
    }

    @Override
    public boolean sendRegEmail(UserRequest userRequest) {
        String mail = userRequest.getMail();
        //1.校验邮箱号
        if (mail.isEmpty()) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "邮箱不能为空！");
        }
        if (!mail.matches(EMAIL_REGEX)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请输入正确的邮箱号");
        }
        //2.发送验证码，这里暂时由随机数生成
        String code = RandomUtil.randomNumbers(CODE_LENGTH);
        log.info("当前验证码为{}", code);
        boolean email = emailUtils.sendMail(mail, code);
        if (!email) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "验证码发送失败");
        }
        //3.将验证码保存到Redis中,并设置有效时常为2分钟
        stringRedisTemplate.opsForValue().set(VER_CODE + mail, code, 2, TimeUnit.MINUTES);
        return true;
    }

    @Override
    public boolean sendPhoneCode(UserRequest userRequest) {
        String phone = userRequest.getPhone();
        //1.校验手机号
        if (phone.isEmpty()) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "手机号不能为空！");
        }
        if (!phone.matches(PHONE_REGEX)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请输入正确的手机号");
        }
        //2 手机号验证码
        String code = RandomUtil.randomNumbers(CODE_LENGTH);
        try {
            SendSmsConfig.getSendSms(phone, code);
            log.info("当前验证码为{}", code);
        } catch (Exception e) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "验证码发送失败");
        }
        stringRedisTemplate.opsForValue().set(VER_CODE + phone, code, 1, TimeUnit.MINUTES);
        return true;
    }


    @Override
    public String userRegister(UserRequest userRequest) {
        String phone = userRequest.getPhone();
        String verCode = userRequest.getVerCode();
        if (StringUtils.isAnyBlank(phone, verCode)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "手机或验证码不能为空！");
        }
        //是否已注册
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.eq(BY_PHONE,phone);
        User selectOne = userMapper.selectOne(userQueryWrapper);
        if(selectOne != null){
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "该手机已注册！");
        }
        String code = stringRedisTemplate.opsForValue().get(VER_CODE + phone);
        if(StringUtils.isBlank(code)){
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "验证码已过期请重新获取！");
        }
        if (!verCode.equals(code)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "验证码错误！");
        }
        //注册成功，随机生成账号,默认密码、头像
        String end = RandomUtil.randomNumbers(ACCOUNT_LENGTH);
        String newAccount = PREFIX + end;
        User user = new User();
        user.setPhone(phone);
        user.setAvatar(PIC);
        user.setAccount(newAccount);
        user.setUserName(RandomUtil.randomString(8));
        //默认密码为手机号，前端提示尽快修改
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + phone).getBytes());
        user.setPassword(encryptPassword);
        int insert = userMapper.insert(user);
        if(insert == 0){
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "数据库发生错误，请稍后再试！");
        }
        return newAccount;
    }

   /* @Override
    public String userRegisterByEmail(UserRequest userRequest) {
        String mail = userRequest.getMail();
        String verCode = userRequest.getVerCode();
        if (StringUtils.isAnyBlank(mail, verCode)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "邮箱或验证码不能为空！");
        }
        //是否已注册
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.eq(BY_EMAIL,mail);
        User selectOne = userMapper.selectOne(userQueryWrapper);
        if(selectOne != null){
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "该邮箱已注册！");
        }
        String code = stringRedisTemplate.opsForValue().get(VER_CODE + mail);
        if (!verCode.equals(code)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "验证码错误！");
        }
        //注册成功，随机生成账号,默认密码、头像
        String end = RandomUtil.randomNumbers(ACCOUNT_LENGTH);
        String newAccount = PREFIX + end;
        User user = new User();
        user.setMail(mail);
        user.setAvatar(PIC);
        user.setAccount(newAccount);
        user.setUserName(RandomUtil.randomString(8));
        //默认密码为邮箱号，前端提示尽快修改
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + mail).getBytes());
        user.setPassword(encryptPassword);
        int insert = userMapper.insert(user);
        if(insert == 0){
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "数据库发生错误，请稍后再试！");
        }
        return newAccount;
    }*/


    @Override
    public UserVO userUpdateBasic(UserRequest userRequest) {
        UserVO loginUser = UserHolder.getUser();
        String userName = userRequest.getUserName();
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.eq("user_name",userName);
        User userChange = userMapper.selectOne(userQueryWrapper);
        if(userChange!=null&&!loginUser.getId().equals(userChange.getId() )){
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "该昵称已存在！");
        }
        //注意前端传过来的格式
        Date birthday = userRequest.getBirthday();
        String gender = userRequest.getGender();
        User user = new User();
        user.setId(loginUser.getId());
        user.setUserName(userName);
        user.setBirthday(birthday);
        user.setGender(gender);
        int update = userMapper.updateById(user);
        if(update == 0){
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "数据库发生错误，请稍后再试！");
        }
        User userUpdate = userMapper.selectById(loginUser.getId());
        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(userUpdate,userVO);
        return userVO;
    }

    @Override
    public String userUpdatePic(MultipartFile file) {
        String fileStr = AliyunOSSConfig.OSSUploadFile(file);
        UserVO userVO = UserHolder.getUser();
        //获取原先头像
        String avatarOld = userVO.getAvatar();
        User user = new User();
        user.setId(userVO.getId());
        user.setAvatar(fileStr);
        int updateById = userMapper.updateById(user);
        if(updateById<=0){
            throw new BusinessException(ErrorCode.OPERATION_ERROR,"图片更新有误");
        }
        //旧照片先不删，存入Redis按期统一处理
        //AliyunOSSConfig.OSSDeleteFile(avatarOld);
        stringRedisTemplate.opsForValue().set("USER_OLDER_PIC:"+ user.getId()+LocalDateTime.now(),avatarOld);
        return fileStr;
    }

    @Override
    public Page<UserVO> getUserList(UserSearchRequest userSearchRequest) {
        long pageSize = userSearchRequest.getPageSize();
        long current = userSearchRequest.getCurrent();
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        String userName = userSearchRequest.getUserName();
        if(StringUtils.isNotBlank(userName)){
            userQueryWrapper.like("user_name",userName);
        }
        String account = userSearchRequest.getAccount();
        if(StringUtils.isNotBlank(account)){
            userQueryWrapper.like("account",account);
        }
        PageDTO<User> userRecords = this.page(new PageDTO<>(current, pageSize), userQueryWrapper);
        List<User> records = userRecords.getRecords();
        List<UserVO> userVOList = records.stream()
                .map(user -> {
                    UserVO userVO = new UserVO();
                    BeanUtils.copyProperties(user, userVO);
                    return userVO;
                })
                .toList();
        PageDTO<UserVO> userVOPageDTO = new PageDTO<>(userRecords.getCurrent(),userRecords.getSize(), userRecords.getTotal());
        userVOPageDTO.setRecords(userVOList);
        return userVOPageDTO;
    }

    @Override
    public Boolean toBanUser(Long id) {
        User user = userMapper.selectById(id);
        User userNew = new User();
        userNew.setId(id);
        if(user.getIsBan() == 0){
            //禁封
            userNew.setIsBan(1);
        }else{
            //解禁
            userNew.setIsBan(0);
        }
        int updateById = userMapper.updateById(userNew);
        return updateById>0;
    }

    /**
     * 获取登录方式
     *
     * @param userRequest userRequest
     * @return String
     */
    private static String getAdapter(UserRequest userRequest) {
        String loginAdapter = userRequest.getLoginAdapter();
        if (StringUtils.isBlank(loginAdapter)) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "请选择登录方式");
        }
        return loginAdapter;
    }


    @Override
    public Boolean userLogout(HttpServletRequest request) {
        String token = request.getHeader("authorization");
        if (token == null) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "未登录");
        }
        // 移除登录态
        return Boolean.TRUE.equals(stringRedisTemplate.delete(LOGIN_USER_KEY + token));
    }


}
